1 /************************************************************
2 * Copyright *
3 * Portions of this software are Copyright (c) 1993 - 2002, *
4 * Chad Z. Hower (Kudzu) and the Indy Pit Crew *
5 * - http://www.nevrona.com/Indy/ *
6 ************************************************************/
7 package org.indy;
8
9 import java.net.InetAddress;
10 import java.net.UnknownHostException;
11
12 import java.text.MessageFormat;
13
14 import java.util.Collection;
15 import java.util.HashSet;
16 import java.util.Iterator;
17
18 import org.indy.io.IndyUnknownHostException;
19
20
21 /***
22 * <code>IndyComponent</code> is the ancestor of all Indy components
23 * which implement client or server functionality.<p>
24 *
25 * <code>IndyComponent</code> provides a convinient method to acess the local
26 * machine's host name with {@link getLocalName()}, and is also responsible for
27 * firing events to registered instances of {@link IndyComponentListener} to signal state change, input,
28 * and output operations in the component.
29 *
30 *@author OTG
31 *@version 1.0
32 *@see IndyComponentListener
33 */
34 public abstract class IndyComponent extends BaseComponent {
35 /***
36 * Cached version of local host name for internal use
37 */
38 private static String cachedHostName;
39
40 /***
41 * Internal collection to hold component listeners. Currently uses a hash set to prevent duplicates.
42 */
43 private Collection listeners = new HashSet();
44
45 /***
46 * Internal array of workInfos, to be the same size as there are elements in {@link WorkMode}.
47 * Used to support nesting of work events.
48 *
49 * @todo Find a better way of doing this?
50 */
51 private final WorkInfo[] workInfos = { new WorkInfo(), new WorkInfo() };
52
53 /***
54 * Returns the local host name of this machine.
55 * <p>
56 * NOTE: This value is lazily created and then cached, so that, at present,
57 * changes to the host name will not update.
58 * <p>
59 * This method provides a shortcut for
60 * <pre>
61 * java.net.InetAddress.getLocalHost().getHostName();
62 * </pre>
63 *
64 *@return The host name of the local machine.
65 *@throws IndyUnknownHostException if, for some reason, the local host can not be resolved
66 *@throws SecurityExeption if a security manager exists and <code>checkConnect</code> doesn't allow this operation.
67 */
68 public final static String getLocalName() throws IndyUnknownHostException {
69 try {
70 if (cachedHostName == null) {
71 cachedHostName = InetAddress.getLocalHost().getHostName();
72 }
73 }
74 catch (UnknownHostException ex) {
75 throw new IndyUnknownHostException(ex);
76 }
77
78 return cachedHostName;
79 }
80
81 /***
82 * Returns this instance's <code>workInfo</code> object for the appropriate work mode.
83 *
84 *@param workMode The work mode to retreive the workInfo for
85 *@return The workInfo in question.
86 */
87 private WorkInfo workInfoForType(WorkMode workMode) {
88 return workInfos[workMode.type()];
89 }
90
91 /***
92 * Fires a status event with the given {@link Status}.
93 *
94 * This is a shortcut for
95 * <pre>
96 * doStatus(status, new Object[0]);
97 * </pre>
98 *
99 *@param status The <code>Status</code> that represents the state being changed to.
100 *@see Status
101 */
102 protected final void doStatus(Status status) {
103 doStatus(status, new Object[0]);
104 }
105
106 /***
107 * Fires a state change event for a given {@link Status},
108 * taking an array of arguments for message formatting.
109 *
110 * The message formatting uses java.text.MessageFormat, and the appropriate resource string
111 * for the <code>Status</code> in question.
112 *
113 *@param status The <code>Status</code> being changed to.
114 *@param args Arguments to format the state change message with.
115 */
116 protected final void doStatus(Status status, Object[] args) {
117 String msg = MessageFormat.format(status.toString(), args);
118
119 Iterator i = listeners.iterator();
120
121 while (i.hasNext()) {
122 ((IndyComponentListener) i.next()).onStatus(this, status, msg);
123 }
124 }
125
126 /***
127 * Registers an {@link IndyComponentListener} to receive events from this component instance.
128 *
129 *@param listener The <code>IndyComponentListener</code> to receive events from this component.
130 *@see IndyComponentListener
131 */
132 public void addComponentListener(IndyComponentListener listener) {
133 listeners.add(listener);
134 }
135
136 /***
137 * Unregisters an {@link IndyComponentListener} from this component.
138 *
139 *@param listener The <code>IndyComponentListener</code> to remove.
140 *@see IndyComponentListener
141 */
142 public void removeComponentListener(IndyComponentListener listener) {
143 listeners.remove(listener);
144 }
145
146 /***
147 * Called to indicate the beginning of work of the given {@link WorkMode}.
148 * Calls can be nested, but each begin must have a corresponding end.
149 *
150 * Only the first beginWork in any nested set for a given <code>WorkMode</code> will fire a {@link IndyComponentListener#onBeginWork(IndyComponent,WorkMode,int)}
151 * event.
152 *
153 *@param workMode The <code>WorkMode</code> of this work.
154 *@param size The size of the work - viz. how many bytes are being read, written or processed.
155 *@see doBeginWork(WorkMode)
156 *@see doEndWork(WorkMode)
157 *@see doWork(WorkMode,int)
158 *@see IndyComponentListener
159 *@see IndyComponentListener#onBeginWork(IndyComponent,WorkMode,int)
160 */
161 protected void doBeginWork(WorkMode workMode, int size) {
162 if (!(workMode.type() > workInfos.length - 1)) {
163 workInfoForType(workMode).level++;
164
165 if (workInfoForType(workMode).level == 1) {
166 workInfoForType(workMode).max = size;
167 workInfoForType(workMode).current = 0;
168
169 Iterator i = listeners.iterator();
170
171 while (i.hasNext()) {
172 ((IndyComponentListener) i.next()).onBeginWork(this, workMode, size);
173 }
174 }
175 }
176 }
177
178 /***
179 * Calls to signal the beginning of work of a given {@link WorkMode}.
180 *
181 * See {@link doBeginWork(WorkMode,int} for more details.
182 *
183 * This method is a shortcut for
184 * <pre>
185 * doBeginWork(workMode,0);
186 * </pre>
187 *
188 *@param workMode The <code>WorkMode</code> describing the type of operation underway.
189 *@see doBeginWork(WorkMode,int)
190 *@see doEndWork(WorkMode)
191 *@see doWork(WorkMode,int)
192 *@see IndyComponentListener
193 *@see IndyComponentListener#onBeginWork(IndyComponent,WorkMode,int)
194 */
195 protected final void doBeginWork(WorkMode workMode) {
196 doBeginWork(workMode, 0);
197 }
198
199 /***
200 * Signals that a piece of work of the given {@link WorkMode} is underway
201 * to the tune of <code>count</code> bytes.
202 *
203 * This call will have no effect unless a corresponding call to begin work has been made;
204 * otherwise a {@link IndyComponentListener#onWork(IndyComponent, WorkMode, int) onWork}
205 * event will be fired to all interested parties.
206 *
207 *@param workMode The <code>WorkMode</code> that this work represents.
208 *@param count The number of bytes being processed.
209 *@see doBeginWork(WorkMode,int)
210 *@see doEndWork(WorkMode,int)
211 *@see WorkMode
212 *@see IndyComponentListener
213 *@see IndyComponentListener#onWork(IndyComponent, IndyComponent.WorkMode, int)
214 *@todo Should this throw exceptions if count is > workInfo.max? Or if there has been no doBeginWork call?
215 */
216 protected final void doWork(WorkMode workMode, int count) {
217 if (!(workMode.type() > workInfos.length - 1)) {
218 if (workInfoForType(workMode).level > 0) {
219 workInfoForType(workMode).current += count;
220
221 Iterator i = listeners.iterator();
222
223 while (i.hasNext()) {
224 ((IndyComponentListener) i.next()).onWork(this, workMode, count);
225 }
226 }
227 }
228 }
229
230 /***
231 * Signals that a piece of work begun by {@link doBeginWork(WorkMode,int) doBeginWork}
232 * has ended.
233 *
234 * Each call to <code>doBeginWork</code> should have a corresponding call to <code>doEndWork</code>,
235 * and, as with <code>doBeginWork</code> only the innovation representing the outermost portion of
236 * work in a nested call will trigger an event, in this case {@link IndyComponentListener#onEndWork(IndyComponent,WorkMode)}.
237 *
238 *@param workMode The <code>WorkMode</code> for which work is ending.
239 *@see doBeginWork(WorkMode,int)
240 *@see doBeginWork(WorkMode)
241 *@see doWork(WorkMode,int)
242 *@see WorkMode
243 *@see IndyComponentListener
244 *@see IndyComponentListener#onEndWork(IndyComponent,WorkMode)
245 */
246 protected final void doEndWork(WorkMode workMode) {
247 if (!(workMode.type() > workInfos.length - 1)) {
248 if (workInfoForType(workMode).level == 1) {
249 Iterator i = listeners.iterator();
250
251 while (i.hasNext()) {
252 ((IndyComponentListener) i.next()).onEndWork(this, workMode);
253 }
254 }
255
256 workInfoForType(workMode).level--;
257 }
258 }
259
260 /***
261 * An enumerated class representing the possible work types for siganalling work events
262 * in an <code>IndyComponent<code>. <p>
263 *
264 *@author OTG
265 *@version 1.0
266 *@see IndyComponent
267 *@see IndyComponent#doBeginWork(WorkMode,int)
268 *@see IndyComponent#doBeginWork(WorkMode)
269 *@see IndyComponent#doWork(WorkMode,int)
270 *@see IndyComponent#doEndWork(WorkMode)
271 *@see IndyComponentListener
272 *@see IndyComponentListener#onBeginWork(IndyComponent,WorkMode,int)
273 *@see IndyComponenyListener#onWork(IndyComponent,WorkMode,int)
274 *@see IndyComponenyListener#onEndWork(IndyComponent,WorkMode)
275 */
276 public static final class WorkMode {
277 /***
278 * The component is reading data from the peer connection.
279 */
280 public final static WorkMode READ = new WorkMode(0);
281
282 /***
283 * The component is writing data to the peer connection
284 */
285 public final static WorkMode WRITE = new WorkMode(1);
286 private int _type;
287
288 private WorkMode(int type) {
289 _type = type;
290 }
291
292 /***
293 * A hack to allow the use of the workInfo[] for tracking work.
294 */
295 private int type() {
296 return _type;
297 }
298 }
299
300 /***
301 * Used internally in {@link IndyComponent} for tracking calls to <code>doBeginWork</code>,<code>doWork</code> and <code>doEndWork</code>
302 *
303 * @author OTG
304 * @version 1.0
305 */
306 private class WorkInfo {
307 /***
308 * The current bytes worked upon in this chunk of work
309 */
310 private int current;
311
312 /***
313 * The nesting level of this chunk of work
314 */
315 private int level;
316
317 /***
318 * The overall number of bytes associated with this chunk of work
319 */
320 private int max;
321 }
322 }
This page was automatically generated by Maven